在我們的簡易銀行專案的當前代碼中,我們在**main_test.go
文件中硬編碼了dbDriver
、dbSource
等常數,並在main.go
文件中為serverAddress
hard code的**常數。
db/sqlc/main_test.go
const (
dbDriver = "postgres"
dbSource = "postgresql://root:secret@localhost:5432/simple_bank?sslmode=disable"
)
main.go
const (
dbDriver = "postgres"
dbSource = "postgresql://root:secret@localhost:5432/simple_bank?sslmode=disable"
serverAddress = "0.0.0.0:8080"
)
go.mod
**文件中,我們可以看到Viper已被添加為依賴項。go get github.com/spf13/viper
我將創建一個新文件**app.env
來存儲我們的開發配置值。接著,我們從main.go
**文件中複製這些變數並將它們copy
到此配置文件中。
我們使用的是dot env format
,因此必須更改聲明這些變數的方式。
app.env
DB_DRIVER=postgres
DB_SOURCE=postgresql://root:secret@localhost:5432/simple_bank?sslmode=disable
SERVER_ADDRESS=0.0.0.0:8080
在**util
包中創建一個新文件config.go
**。
在此文件中聲明一個新類型的**Config
結構。這個Config
**結構將保存從文件或環境變數中讀取的應用程序的所有配置變數。
我們必須使用與在**app.env
中宣告的每個變數的確切名稱**。例如,DBDriver
的標籤名稱應該是DB_DRIVER,DBSource
的標籤名稱應該是DB_SOURCE,同樣地,ServerAddress
的標籤名稱應該是SERVER_ADDRESS。
Viper 在底層使用**mapstructure
包進行unmarshaling
,因此我們使用mapstructure
**標籤來指定每個配置字段的名稱。
util/config.go
type Config struct {
DBDriver string `mapstructure:"DB_DRIVER"`
DBSource string `mapstructure:"DB_SOURCE"`
ServerAddress string `mapstructure:"SERVER_ADDRESS"`
}
接著,定義一個新函數**LoadConfig()
**,它接受一個path
作為輸入,並返回一個配置對象或一個錯誤。
這個函數將從path
中的配置文件中讀取配置,或者如果提供了環境變數,則覆蓋它們的值。
首先,我們呼叫viper.AddConfigPath()
以告知Viper配置文件的位置。在此情況下,位置由輸入路徑參數給定。
接著,我們呼叫viper.SetConfigName()
來告訴Viper尋找具有特定名稱的配置文件。我們的配置文件是app.env
,所以它的名稱是app
。
我們還通過呼叫viper.SetConfigFile()
並傳入env
來告知Viper配置文件的類型。在這種情況下是env
。如果需要,也可以使用JSON、XML或其他任何格式,只需確保您的配置文件格式和擴展名正確。
除了從文件讀取配置外,我們還希望viper從環境變數讀取值。因此,我們呼叫viper.AutomaticEnv()
告知viper自動使用對應的環境變數值覆蓋它從配置文件讀取的值(如果存在的話)。
之後,我們呼叫viper.ReadInConfig()
開始讀取配置值。如果錯誤不為nil,則我們簡單地返回它。
否則,我們呼叫viper.Unmarshal()
將值解封裝到目標配置對象中。最後,僅返回配置對象及其任何發生的錯誤。
總的來說,加載配置功能已完成。現在,我們可以在main.go
文件中使用它。
err
是作為函數的返回值之一被預先定義的。當您在函數簽名中定義了返回值的名稱(在這裡是 config
和 err
),那麼在函數體內部,這些變數已經被初始化並可以直接使用。
// LoadConfig從文件或環境變數讀取配置。
func LoadConfig(path string) (config Config, err error) {
viper.AddConfigPath(path)
viper.SetConfigName("app")
viper.SetConfigType("env")
viper.AutomaticEnv()
err = viper.ReadInConfig()
if err != nil {
return
}
err = viper.Unmarshal(&config)
return
}
main.go
所有的Hard Code
main()
函數中,呼叫util.LoadConfig()
,並在此處傳入".",這表示當前文件夾,因為我們的配置文件app.env
與這個main.go
文件位於相同的位置。cannot load config
"的log。否則,我們只需將這些變量更改為config.DBDriver
、config.DBSource
和config.ServerAddress
。main.go
func main() {
config, err := util.LoadConfig(".")
if err != nil {
log.Fatal("cannot load config:", err)
}
conn, err := sql.Open(config.DBDriver, config.DBSource)
if err != nil {
log.Fatal("cannot connect to db:", err)
}
store := db.NewStore(conn)
server := api.NewServer(store)
err = server.Start(config.ServerAddress)
if err != nil {
log.Fatal("cannot start server:", err)
}
}
localhost
的8080
端口,這與我們在app.env
文件中指定的一致。app.env
文件中的設定監聽在localhost
的8080
端口。make server
go run main.go
func TestMain(m *testing.M) {
var err error
config, err := util.LoadConfig("../..")
if err != nil {
log.Fatal("cannot load config:", err)
}
testDB, err = sql.Open(config.DBDriver, config.DBSource)
if err != nil {
log.Fatal("cannot connect to db: ", err)
}
testQueries = New(testDB)
exitCode := m.Run()
os.Exit(exitCode)
}